﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<cstdlib>
#include<cstdio>
#include<time.h>
#include<conio.h>
#include<iostream>
#include<list>
#include <windows.h>
#define datalen sizeof(int)
int** p1 = NULL;
int** p2 = NULL;
int trynum = 0;
const int BNM = 5;
std::list<int**> backn;
//数组指针，与打印部分相匹配

//设置两个整形指针，用于存放备份的内容
void menu()
{
	printf("-----------欢迎来到2048的世界-----------\n");
	printf("-本游戏版权所有@火山@copyright2024~2027-\n");
	printf("请选择一项：0.退出程序 1.开始游戏 2.查看历史成绩纪录\n");
}

//支持的撤销操作,需要存储棋盘以作为媒介

void backward(int*** tribpointer)
{
	*tribpointer = backn.back();
	if (backn.size()) backn.pop_back();
	else return;
}

//棋盘的备份操作，用malloc创建一块空间，随后返回这块空间的地址
int** back_up(int** board)
{
	int**p = (int**)malloc(sizeof(int*)*4);
	if (p == NULL)
	{
		perror("error opening new space!");
	}
	else
	{
		for (int i = 0; i < 4; i++)
		{
			p[i] = (int*)malloc(sizeof(int) * 4);
			//使用二级指针的方式开辟二维数组
		}
		if (*p != NULL)//拷贝数组中的元素
		{
			for (int i = 0; i < 4; i++)
			{
				for (int j = 0; j < 4; j++)
				{
					p[i][j] = board[i][j];
				}
			}
		}
	}
	return p;
	
}











//查看历史记录
void history_res()
{

	FILE* pf = (FILE*)fopen("history.txt", "r");
	if (pf== NULL)
	{
		printf("您还没有开始游戏哦，亲\n");
	
	}
	else
	{
		char ch;
		while((ch = fgetc(pf))!=EOF)
		{
				printf("%c", ch);
		}

		fclose(pf);
		pf = NULL;
	}

	
}


//游戏结束后记录历史记录
void history_rec(int score)
{
	FILE* pf = (FILE*)fopen("history.txt", "a");
	if (pf == NULL)
		perror("eorro opening file:");
	else
	{
		
		time_t timep;
		struct tm* p;
		char time_str[50] ;
		time(&timep);
		p = gmtime(&timep);
		sprintf(time_str, "%d年%d月%d日 %d:%02d:%02d    分数:%d\n\n", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, 8 + p->tm_hour, p->tm_min, p->tm_sec, score);
		printf("%s\n", time_str);

		fprintf(pf, time_str);
		fclose(pf);
		pf = NULL;
	}
	
}





//2048棋盘的初始化
void boardinit(int **board)
{
	int i, j;
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			board[i][j] = 0;
		}
	}
}

//2048棋盘的打印或者输出
void displayboard(int** board)
{
	int i, j;
	printf("-------------------------\n");
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			if (board[i][j] == 0)
			{
				printf("     |");
			}
			else
			{
				printf("%5d|",board[i][j]);
			}
		}

		printf("\n\n");
	}
	printf("-------------------------\n");
}


//要设置的数字(获得数字)（2或者4）
int getnumber()
{
	srand((unsigned int)time(NULL));
	while (1)
	{
		int a = 2 * (rand() % 3);
		if (a != 0)
		{
			return a;
		}
	}

}


//检查当前棋盘是否已经满容
//1满 0未满
int  full_check(int** board)
{
	int i, j;
	int full = 1;
	for (i = 0; i < 4; i++)
	{
		for (j = 0; j < 4; j++)
		{
			if (board[i][j] == 0)
			{
				full = 0;
			}

		}
	}
	return full;
}




//设置数字
void setnumber(int** board,int  a)
{
	srand((unsigned int)time(NULL));
	int full = full_check(board);
	while (full!=1)
	{
		int i = rand() % 4;
		int j = rand() % 4;
		if (board[i][j] == 0)
		{
			board[i][j] = a ;
			break;
		}
	}
	
}


//向上移动的操作
void move_up(int** board)
{
	//棋盘先进行备份再实行操作
	int** temp = back_up(board);
	backn.push_back(temp);
	if (backn.size() > BNM) backn.pop_front();


	//实现所有数据都向上移动
	int i, j,m;
	for (j = 0; j < 4; j++)
	{
		for (m = 0; m < 3; m++)
		{
			for (i = 1; i < 4; i++)
			{
				if (board[i][j] != 0&& board[i - 1][j] ==0)
				{
					board[i - 1][j] = board[i][j];
					board[i][j] = 0;
				}

			}
		}
	}
	//检查分析是否有合适的数据可以以进行合并(注意先后顺序)
	//合并后再进行将数据向上移动
	for (j = 0; j < 4; j++)
	{
		for (i = 0; i <=2; i++)
		{
			if ((board[i][j] == board[i + 1][j])&&board[i][j]!=0)
			{
				board[i][j] = 2 * board[i][j] ;
				board[i + 1][j] =0;
				for (m = i + 1; m < 3; m++)
				{
					board[m][j] = board[m+1][j];
					board[m + 1][j] =0;
				}
			}
		}
	}



}





//向下移动的操作
void move_down(int** board)
{
	int** temp = back_up(board);
	backn.push_back(temp);
	if (backn.size() > BNM) backn.pop_front();

	int i, j, m;
	for (j = 0; j < 4; j++)
	{
		for (m = 0; m < 3; m++)
		{
			for (i = 2; i >=0; i--)
			{
				if (board[i][j] != 0 && board[i + 1][j] == 0)
				{
					board[i + 1][j] = board[i][j];
					board[i][j] = 0;
				}

			}
		}
	}
	//合并
	for (j = 0; j < 4; j++)
	{
		for (i = 3; i >=1; i--)
		{
			if ((board[i][j] == board[i - 1][j]) && board[i][j] != 0)
			{
				board[i][j] = 2 * board[i][j];
				board[i - 1][j] = 0;
				for (m = i - 1; m >=1; m--)
				{
					board[m][j] = board[m - 1][j];
					board[m - 1][j] = 0;
				}
			}
		}
	}
}



//向左移动的操作
void move_left(int** board)
{

	//棋盘先进行备份再实行操作
	//棋盘先进行备份再实行操作
	int** temp = back_up(board);
	backn.push_back(temp);
	if (backn.size() > BNM) backn.pop_front();


	int i, j, m;
	for (i = 0; i < 4; i++)
	{
		for (m = 0; m < 3; m++)
		{
			for (j = 1; j <=3; j++)
			{
				if (board[i][j] !=0 && board[i][j-1] == 0)
				{
					board[i][j-1] = board[i][j];
					board[i][j] = 0;
				}

			}
		}
	}


//合并，向左的合并操作，合并完成后再次进行移动
	for ( i= 0; i < 4; i++)
	{
		for (j = 0; j <=2; j++)
		{
			if ((board[i][j] == board[i][j+1]) && board[i][j] != 0)
			{
				board[i][j] = 2 * board[i][j];

				board[i][j+1] = 0;
				for (m = j + 1; m<=2; m++)
				{
					board[i][m] = board[i][m+1];
					board[i][m+1] = 0;
				}
			}
		}
	}

}


//向右移动的操作
void move_right(int** board)
{
	//棋盘先进行备份再实行操作
	//棋盘先进行备份再实行操作
	int** temp = back_up(board);
	backn.push_back(temp);
	if (backn.size() > BNM) backn.pop_front();
	int i, j, m;
	for (i = 0; i < 4; i++)
	{
		for (m = 0; m < 3; m++)
		{
			for (j = 2; j >=0; j--)
			{
				if (board[i][j] != 0 && board[i][j +1] == 0)
				{
					board[i][j +1] = board[i][j];
					board[i][j] = 0;
				}

			}
		}
	}



	//合并，操作同上
	for (i = 0; i < 4; i++)
	{
		for (j = 3; j >=1; j--)
		{
			if ((board[i][j] == board[i][j - 1]) && board[i][j] != 0)
			{
				board[i][j] = 2 * board[i][j];
				board[i][j - 1] = 0;
				for (m = j - 1; m >=1; m--)
				{
					board[i][m] = board[i][m - 1];
					board[i][m - 1] = 0;
				}
			}
		}
	}
}


//在棋盘完全充满时，检查对当前游戏是否还可以继续，如果可以则返回1，否则返回0
//1可以移动，0不行
int move_check(int** board)
{
	int a=0;
	int i, j;
	if (full_check(board))
	{
		//按行查看
		for (i = 0; i <= 3; i++)
		{
			for (j = 0; j <= 2; j++)
			{
				if (board[i][j] == board[i][j + 1])
				{
					a = 1;
				}
			}
		}
		//按列查看
		for (j = 0; j <= 3; j++)
		{
			for (i = 0; i <= 2; i++)
			{
				if (board[i][j] == board[i + 1][j])
				{
					a = 1;
				}
			}
		}
	}
	else
		a = 1;
	return a;
}


//
void f_over(int _max)
{
	printf("很抱歉，游戏失败，再接再厉!\n");
	printf("您的最高分数是%d\n", _max);
	std::cout << "程序即将在五秒内退出" << std::endl;
	Sleep(5000);
}



void w_over()//输出游戏成功信息
{
	printf("恭喜你！游戏成功，您可以可以选择继续游戏\n");
	Sleep(1000);
	printf("真心感谢您的游玩，祝您工作学习顺利!\n\n\n");
	Sleep(1000);
	printf("ps:为衷心感谢您，找火山领取'25元'通关奖励\n");
	Sleep(1000);
	printf("版权所有@火山2025.3.4\n");
	Sleep(1000);
	printf("本人保留所有对本应用的著作权\n");
	Sleep(1000);
	std::cout << "程序即将在五秒内退出" << std::endl;
	Sleep(5000);
}



//获得当前游戏的最大值
int getmax(int** board)
{

	int i, j;
	int max = board[0][0];
	for (i = 0; i <= 3; i++)
	{
		for (j = 0; j <= 3; j++)
		{
			if ( max < board[i][j])
			{
				max = board[i][j];
			}
		}
	}
	return max;
}








//游戏检查，检查当前游戏是否已经达到了2048或者已经无法移动两者都会直接结束当前游戏
//如果无法移动或者已经成功则返回1，发否则返回0(即游戏结束）
int  game_over(int** board)
{

	int flag=1;
	int _max=getmax(board);
	if (move_check(board) == 0 && full_check(board) == 1)//无法移动并且容量已满
	{
		f_over(_max);//输出游戏失败信息
		return flag;//游戏结束
	}
	else if (_max == 2048)
	{
		w_over();//输出游戏成功信息
		return flag;//此处是达到了2048最大值，游戏成功
	}
	else
		return 0;
}
void try_try()
{
	if (trynum < 5)
	{
		printf("友情提示：您还没有步骤可以后退哦~\n");
		trynum++;
	}
	else if (trynum >= 5 && trynum < 20)
	{
		printf("请进行游戏后在进行回退！\n");
		trynum++;
	}
	else if (trynum > 20 && trynum <= 30)
		printf("火山温馨提示：不要玩火哦(￣y▽,￣)╭ \n");
	else if (trynum > 30 && trynum <= 50)
	{
		printf("盲生，你发现了华点！！\n\
							    找火山领取10元红包，数量仅限1份，先到先得\n");
	}
	else
	{
		printf("自己来领三十大板！你的10元没了！\n");
		return;
	}
}

//游戏主体部分
void  game()
{
	srand((unsigned int)time(NULL));
	int** board = (int**)malloc(sizeof(int*) * 4);
	if (board == NULL)
	{
		perror("error opening new space!");
	}
	else 
	{
		for (int i = 0; i < 4; i++)
		{
			board[i] = (int*)malloc(sizeof(int) * 4);
			if (board[i] == NULL)
			{
				perror("error opening new space!");
			}
		}
	}
	boardinit(board);


	int a = getnumber();
	setnumber(board, a);

	int b = getnumber();
	setnumber(board, b);
	displayboard(board);

	char ch;
	do
	{
		printf("请输入操作 w:上 s:下 a:左 d:右\n");
		printf("回退上一步操作：b-撤销上一步操作\n");
		printf("最多支持撤销五步操作\n");
		printf("-------------------------\n");

		std::cin >> ch;//空格不能省略，避免被误读

		switch (ch)
		{
			case 'w':
			{
				move_up(board);
				int a = getnumber();
				setnumber(board, a);
				displayboard(board);
				break;
			}
			case 's':
			{
				move_down(board);
				int a = getnumber();
				setnumber(board, a);
				displayboard(board);
				break;
			}
			case 'a':
			{
				move_left(board);
				int a = getnumber();
				setnumber(board, a);
				displayboard(board);
				break;
			}
			case 'd':
			{
				move_right(board);
				int a = getnumber();
				setnumber(board, a);
				displayboard(board);
				break;
			}
			case 'b':
			{
				if (backn.empty())
				{
					try_try();
				}
				else
				{
					backward(&board);
					displayboard(board);
				}
				break;
			}
			default:
			{
				printf("输入错误，请重新输入:\n\n\n");
				break;
			}
		}
	} while (move_check(board)==1);

	int _max = getmax(board);

	if (move_check(board) == 0&&_max!=2048)
	{
		
		f_over(_max);
	}
	else if (_max == 2048)
	{
		w_over();
	}
	history_rec(_max);
	/*void history_rec(int score)
	{
		FILE* pf = (FILE*)fopen("history.txt", "a");
		if (pf == NULL)
			perror("eorro opening file:");
		time_t timep;
		struct tm* p;
		char time_str[28];
		time(&timep);
		p = gmtime(&timep);
		sprintf(time_str, "%d年%d月%d日 %d:%d:%d    %d", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, 8 + p->tm_hour, p->tm_min, p->tm_sec, score);
		
		//printf("%s\n", time_str);
		fprintf(pf,time_str);
		free(pf);
		pf = NULL;
	}*/


}